package com.kedacom.ctsp.authority.service.simple;


import com.google.common.collect.Lists;
import com.kedacom.ctsp.authority.dao.DepartmentJpaDao;
import com.kedacom.ctsp.authority.dto.DepartmentImportDTO;
import com.kedacom.ctsp.authority.entity.Department;
import com.kedacom.ctsp.authority.service.ChargeRelationService;
import com.kedacom.ctsp.authority.service.DepartmentService;
import com.kedacom.ctsp.authority.util.ImportExcelCommonUtil;
import com.kedacom.ctsp.authority.vo.AuthorityImportExcelResultVo;
import com.kedacom.ctsp.authz.entity.Authentication;
import com.kedacom.ctsp.authz.exception.UnauthorizedException;
import com.kedacom.ctsp.lang.RandomUtil;
import com.kedacom.ctsp.lang.StringUtil;
import com.kedacom.ctsp.lang.Validate;
import com.kedacom.ctsp.lang.exception.CommonException;
import com.kedacom.ctsp.lang.mapper.BeanMapper;
import com.kedacom.ctsp.lang.validate.ValidationException;
import com.kedacom.ctsp.office.excel.ExcelUtil;
import com.kedacom.ctsp.orm.dsl.Query;
import com.kedacom.ctsp.web.entity.TreeSupportBean;
import com.kedacom.ctsp.web.entity.param.QueryParamEntity;
import com.kedacom.ctsp.web.service.StatusEnum;
import com.kedacom.ctsp.web.service.simple.AbstractTreeSortService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static com.kedacom.ctsp.authority.entity.SettingForType.DEPARTMENT;
import static com.kedacom.ctsp.authority.entity.SettingForType.PERSON;


/**
 * 默认的服务实现
 *
 * @author sunchenjie
 */
@Service("departmentService")
public class DepartmentServiceImpl
        extends AbstractTreeSortService<Department, String> implements DepartmentService {
    @Autowired
    private ChargeRelationService chargeRelationService;

    @Autowired
    private DepartmentJpaDao departmentJpaDao;

    @Override
    @Transactional(readOnly = true)
    public List<Department> getChildren(String... departmentId) {
        if (departmentId != null) {
            List<String> ids = Arrays.asList(departmentId);
            if (CollectionUtils.isNotEmpty(ids)) {
                List<Department> depts = createQuery().where().in(Department.ID, ids).listNoPaging();
                Query<Department, QueryParamEntity> query = createQuery();
                depts.stream().forEach(d -> query.or().like$(Department.PATH, d.getPath()));
                //下级部门
                List<Department> deptCascade = query.listNoPaging();

                return deptCascade;
            }
        }
        return null;
    }


    @Override
    public String insert(Department entity) {
        if (entity.getStatus() == null) {
            entity.setStatus(StatusEnum.ENABLE.ordinal());
        }
        // 检测部门编码重复
        if (!checkUnique(Department.CODE, entity.getCode())) {
            throw new ValidationException("auth.dept.code.duplicated");
        }
        // 检测部门名称重复
        // Validate.isTrue(checkNameUnique(vo.getName()), "部门名称不得重复");

        return super.insert(entity);
    }

    @Override
    public String patch(String id, Department entity) {
        Department oldEntity = get(id);
        Validate.notNull(oldEntity, "auth.dept.notexist");

        // 检测部门编码重复
        if (StringUtils.isNotBlank(entity.getCode()) && !oldEntity.getCode().equals(entity.getCode())) {

            if (!checkUnique(Department.CODE, entity.getCode())) {
                throw new ValidationException("auth.dept.code.duplicated");
            }
            if (oldEntity.getRoles() != null && oldEntity.getRoles().size() > 0) {
                throw new ValidationException("auth.dept.roles.duplicated");
            }
        }
        // 检查上级部门满足条件
        int parentChgType = checkDeptParent(id, entity, oldEntity);

        // 更新角色置空
        if (entity.getRoles() != null && entity.getRoles().size() == 0) {
            oldEntity.setRoles(null);
        }

        //更新
        patch(oldEntity, entity, parentChgType);
        return getDao().patch(entity);
    }

    /**
     * patch更新
     *
     * @param oldEntity
     * @param entity
     * @param parentChgType
     */
    private void patch(Department oldEntity, Department entity, int parentChgType) {

        Department parent = null;//新上级部门
        //parent 是否修改
        if (parentChgType > 0) {//变更新新上级
            parent = super.get(entity.getParent().getId());
        } else if (parentChgType < 0) {
            //删上级
            oldEntity.setParent(null);
        }
        if (parentChgType != 0) {
            applyPath(parent, entity, 0);
            applyPathByParent(entity, selectAllChildNode(entity.getId(), QueryParamEntity.empty()));
        }
    }

    private int applyPathByParent(Department parent, List<Department> children) {
        if (CollectionUtils.isNotEmpty(children)) {
            int nextNum = 0;
            for (int i = 0; i < children.size(); i++) {
                Department e = children.get(i);
                applyPath(parent, e, i);
                super.update(e.getId(), e);
                nextNum = nextNum + applyPathByParent(e, e.getChildren());
            }
            return children.size() + nextNum;
        }
        return 0;
    }

    /**
     * 检查上级部门满足条件
     *
     * @param id
     * @param entity
     * @param oldEntity
     * @return -1:删除上级部门 0:上级部门不变 1:变更上级
     */
    private int checkDeptParent(String id, Department entity, Department oldEntity) {

        if (entity.getParent() != null && StringUtils.equals(entity.getParent().getId(), id)) {
            //id和parentId 不能设置相同
            throw new ValidationException("auth.dept.id.parent.id.same.error");
        }

        if (entity.getParent() == null || StringUtils.isBlank(entity.getParent().getId())) {
            //删除上级部门
            entity.setParent(null);
            return -1;
        }

        boolean parentChg = false;//上级部门变更
        if (oldEntity.getParent() == null) {
            if (entity.getParent() != null) {
                //添加上级部门
                parentChg = true;
            }
        } else {
            if (!StringUtils.equals(oldEntity.getParent().getId(), entity.getParent().getId())) {
                //更改新上级部门
                parentChg = true;
            }
        }

        if (!parentChg) {
            return 0;
        }

        //最新上级部门
        Department newParentDept = get(entity.getParent().getId());
        Validate.notNull(newParentDept, "auth.dept.notexist");

        if (newParentDept.getPath().indexOf(oldEntity.getPath()) > -1) {
            throw new ValidationException("auth.dept.child.dept.to.parent.error");
        }

        return 1;
    }


    @Override
    public List<? extends DepartmentImportDTO> importDepartments(List<? extends DepartmentImportDTO> deptsDtos) {
        Authentication authentication = Authentication.current().orElseThrow(UnauthorizedException::new);
        int i = 2;
        for (DepartmentImportDTO di : deptsDtos) {
            Department entity = BeanMapper.deepMap(di, Department.class);

            if (StringUtils.isBlank(di.getCode())) {
                di.getErrors().add(StringUtil.format("auth.dept.code.required"));
            }
            if (StringUtils.isBlank(di.getName())) {
                di.getErrors().add(StringUtil.format("auth.dept.name.required"));
            }
            long total = createQuery().where(Department.CODE, di.getCode()).total();
            if (total > 0) {
                di.getErrors().add(StringUtil.format("auth.dept.code.duplicated"));
            }
            if (StringUtils.isNotBlank(di.getParentCode())) {
                List<Department> parentDepts = createQuery().where(Department.CODE, di.getParentCode()).listNoPaging();
                if (CollectionUtils.isEmpty(parentDepts)) {
                    di.getErrors().add(StringUtil.format("auth.dept.parent.notexist"));
                } else {
                    entity.setParent(parentDepts.get(0));
                }
            } else {
                entity.setParent(null);
            }
            di.setRownum(i++);
            if (CollectionUtils.isEmpty(di.getErrors())) {
                try {

                    if (authentication.getPerson() != null) {
                        entity.setCreateDeptCode(authentication.getPerson().getDeptCode());
                    }
                    if (authentication.getUser() != null) {
                        entity.setCreatorCode(authentication.getUser().getUsername());
                    }
                    insert(entity);
                } catch (CommonException e) {
                    logger.error("部门导入出错", e);
                    di.getErrors().add(e.getMessage());
                } catch (Exception e) {
                    logger.error("部门导入出错", e);
                    di.getErrors().add(StringUtil.format("excel.import.error") + " : " + e.getMessage());
                }
            }
        }
        return deptsDtos;
    }

    /**
     * 获取部门的下级部门的code
     *
     * @param departmentId
     * @return
     */
    @Override
    public Set<String> getCascadedDepartmentCodes(String departmentId) {

        //根据部门id，获取部门以及下级部门的code
        return getCascadedDepartmentCodesById(departmentId);
    }


    /**
     * 根据部门id，获取部门以及下级部门的code
     *
     * @param departmentId
     * @return
     */
    private Set<String> getCascadedDepartmentCodesById(String departmentId) {

        //获取部门的path
        List<String> deptPath = departmentJpaDao.getDeptPathById(departmentId);
        if (CollectionUtils.isEmpty(deptPath)) {
            return new HashSet<>();
        }

        //根据path，获取下级部门的code
        List<String> deptCodes = departmentJpaDao.getDeptCodeByPathLike(deptPath.get(0));
        return new HashSet(deptCodes);
    }

    @Override
    public Set<String> getCascadedAndChargedDepartmentCodes(String personId, String departmentId) {

        //根据部门id，获取部门分管部门以及下级部门的code
        Set<String> departmentCodes = getCascadedAndChargedDepartmentCodesById(personId);

        //级联部门
        departmentCodes.addAll(getCascadedDepartmentCodesById(departmentId));
        return departmentCodes;
    }


    /**
     * 根据人员id，获取人员分管的部门的code
     *
     * @param personId
     * @return
     */
    private Set<String> getCascadedAndChargedDepartmentCodesById(String personId) {

        // 分管部门
        Set<String> chargeDeptIds = chargeRelationService.getChargeIds(PERSON.ordinal(), personId, DEPARTMENT.ordinal());
        if (CollectionUtils.isEmpty(chargeDeptIds)) {
            return new HashSet<>();
        }

        //分管部门的path
        List<String> deptPaths = departmentJpaDao.getDeptPathByIds(new ArrayList<>(chargeDeptIds));

        //根据分管的部门的path获取分管部门及其子部门的code
        return getChildrenDeptCodeByPath(deptPaths, getDepartmentPathCode());
    }

    /**
     * 根据分管的部门的path获取分管部门及其子部门的code
     *
     * @param deptPaths
     * @return
     */
    private Set<String> getChildrenDeptCodeByPath(List<String> deptPaths, Map<String, String> allPathCodeMap) {

        //记录分管的部门code
        Set<String> deptCodes = new HashSet<>();

        for (Map.Entry<String, String> pathCode : allPathCodeMap.entrySet()) {
            //部门path
            String path = pathCode.getKey();

            for (String checkPath : deptPaths) {
                if (path.indexOf(checkPath) < 0) {
                    continue;
                }
                //记录分管及其子部门的code
                deptCodes.add(pathCode.getValue());
            }
        }
        return deptCodes;
    }

    /**
     * 所有部门path、code 的Map
     *
     * @return
     */
    private Map<String, String> getDepartmentPathCode() {
        //获取部门path和code，Object[0]:path Object[1]:code
        List<Object[]> deptPathCodes = departmentJpaDao.getDeptCodeAndPath();
        if (CollectionUtils.isEmpty(deptPathCodes)) {
            return new HashMap<>();
        }

        //部门path、code 的Map<path、code>
        Map<String, String> pathCodeMap = new HashMap<>();
        for (Object[] pathCode : deptPathCodes) {
            pathCodeMap.put((String) pathCode[0], (String) pathCode[1]);
        }
        return pathCodeMap;
    }


    @Override
    public AuthorityImportExcelResultVo importDepartment(MultipartFile file, HttpServletRequest request) throws Exception {
        Locale locale = LocaleContextHolder.getLocale();
        // 获取excel的数据 分为中文版和国际版
        List<? extends DepartmentImportDTO> departmentImportDTOS;
        if (locale.getLanguage().equalsIgnoreCase("zh")) {
            departmentImportDTOS = ExcelUtil.read2Bean(file.getInputStream(), DepartmentImportDTO.headerMapper, DepartmentImportDTO.class);
        } else {
            departmentImportDTOS = ExcelUtil.read2Bean(file.getInputStream(), DepartmentImportDTO.headerMapper_en, DepartmentImportDTO.class);
        }
        StringBuilder message = new StringBuilder();
        String serialNo = RandomUtil.randomString(32);
        int succCount = 0;
        int failCount = 0;

        if (CollectionUtils.isNotEmpty(departmentImportDTOS)) {

            //真正的导入
            List<? extends DepartmentImportDTO> departmentDtos = importDepartments(departmentImportDTOS);
            // 再次按序号排序
            Collections.sort(departmentDtos, (Comparator<DepartmentImportDTO>) (o1, o2) -> {
                if (o1.getRownum() != null && o2.getRownum() != null) {
                    return o1.getRownum() - o2.getRownum();
                }
                return 0;
            });

            for (DepartmentImportDTO vo : departmentDtos) {
                if (CollectionUtils.isEmpty(vo.getErrors())) {
                    ImportExcelCommonUtil.setMessage(message, vo.getRownum(), vo.getErrors());
                    succCount++;

                } else {
                    ImportExcelCommonUtil.setMessage(message, vo.getRownum(), vo.getErrors());
                    failCount++;

                }
            }
        }

        return ImportExcelCommonUtil.setImportExcelResult(serialNo, succCount, failCount, message);
    }

    @Override
    @Transactional(readOnly = true)
    public Department selectByCode(String code) {
        if (org.apache.commons.lang3.StringUtils.isBlank(code)) {
            return null;
        }
        return createQuery().where(Department.CODE, code).single();
    }

    @Transactional(readOnly = true)
    @Override
    public List<Department> selectByCode(Set<String> deptCodes) {
        if (deptCodes == null || deptCodes.size() == 0) {
            return null;
        }
        return createQuery().in(Department.CODE, deptCodes).listNoPaging();
    }

    @Override
    public Set<String> selectDeptIdByCode(Set<String> deptCodes) {

        List<String> deptIds = departmentJpaDao.getDeptIdsByCodes(new ArrayList<>(deptCodes));
        return new HashSet<>(deptIds);
    }

    @Override
    public List<Department> convertToTree(List<Department> nodes) {
        if (CollectionUtils.isEmpty(nodes)) {
            return Lists.newArrayList();
        }
        Set<String> nodeIds = nodes.stream().map(Department::getId).collect(Collectors.toSet());

        // 设置parentId, 如果不为顶层,但是又是根节点

        // 转换为树形结构
        List<Department> results = TreeSupportBean.list2tree(nodes, Department::setChildren,
                (Predicate<Department>) entity -> isCurRootNode(entity, nodeIds));
        return results;
    }

    /**
     * 是否为当前根节点
     * 1.是顶层
     * 2.不是顶层, 但是 是根节点
     *
     * @return
     */
    private boolean isCurRootNode(Department department, Set<String> nodeIds) {
        // 是顶层
        if (department.getParent() == null || department.getParent().getId() == null) {
            return true;
        }

        // 不是顶层, 但是 是根节点
        return !nodeIds.contains(department.getParent().getId());
    }
}
